package com.wbs.cluster.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wbs.cluster.redis.entity.RedisConfigDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashSet;
import java.util.Set;

/**
 * Created by Administrator on 2019/2/28.
 */
@Configuration
public class RedisClusterConfig2222 {

    @Autowired
    private Environment env;

    private RedisConfigDto getRedisConfigDto() {
        // 获取配置
        RedisConfigDto rct = new RedisConfigDto();
        String cluster = env.getProperty("spring.redis-cluster");
        if ("true".equals(cluster)) {
            //#集群模式
            rct.setRedisCluster(Boolean.parseBoolean(cluster));
            rct.setClusterNodes(env.getProperty("spring.redis.cluster.nodes"));
            rct.setPassword(env.getProperty("spring.redis.password"));
            rct.setDatabase(Integer.parseInt(env.getProperty("spring.redis.database")));
            rct.setMaxRedirects(Integer.parseInt(env.getProperty("spring.redis.cluster.max-redirects")));
            rct.setMaxActive(Integer.parseInt(env.getProperty("spring.redis.lettuce.pool.max-active")));
            rct.setMaxIdle(Integer.parseInt(env.getProperty("spring.redis.lettuce.pool.max-idle")));
            rct.setMinIdle(Integer.parseInt(env.getProperty("spring.redis.lettuce.pool.min-idle")));
            rct.setMaxWait(Integer.parseInt(env.getProperty("spring.redis.lettuce.pool.max-wait")));
            rct.setTimeout(Integer.parseInt(env.getProperty("spring.redis.timeout")));
            rct.setMaxTotal(Integer.parseInt(env.getProperty("redis.maxTotal")));
            rct.setMaxWaitMillis(Integer.parseInt(env.getProperty("redis.maxWaitMillis")));
            rct.setMinEvictableIdleTimeMillis(Integer.parseInt(env.getProperty("redis.minEvictableIdleTimeMillis")));
            rct.setNumTestsPerEvictionRun(Integer.parseInt(env.getProperty("redis.numTestsPerEvictionRun")));
            rct.setTimeBetweenEvictionRunsMillis(Integer.parseInt(env.getProperty("redis.timeBetweenEvictionRunsMillis")));
            rct.setTestOnBorrow(Boolean.parseBoolean(env.getProperty("redis.testOnBorrow")));
            rct.setTestWhileIdle(Boolean.parseBoolean(env.getProperty("redis.testWhileIdle")));
        } else {
            rct.setRedisCluster(Boolean.parseBoolean(cluster));
            rct.setHost(env.getProperty("spring.redis.host"));
            rct.setPort(Integer.valueOf(env.getProperty("spring.redis.port")));
            rct.setDatabase(Integer.valueOf(env.getProperty("spring.redis.database")));
            rct.setPassword(env.getProperty("spring.redis.password"));
            rct.setMaxActive(Integer.valueOf(env.getProperty("spring.redis.jedis.pool.max-active")));
            rct.setMaxIdle(Integer.valueOf(env.getProperty("spring.redis.jedis.pool.max-idle")));
            rct.setMinIdle(Integer.valueOf(env.getProperty("spring.redis.jedis.pool.min-idle")));
            rct.setMaxWait(Integer.valueOf(env.getProperty("spring.redis.jedis.pool.max-wait")));
            rct.setTimeout(Integer.valueOf(env.getProperty("spring.redis.timeout")));
            rct.setTestOnBorrow(Boolean.valueOf(env.getProperty("redis.testOnBorrow")));
        }
        return rct;
    }


    @Bean
    public JedisPoolConfig getJedisPoolConfig() {
        RedisConfigDto rct = getRedisConfigDto();
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大空闲数
        jedisPoolConfig.setMaxIdle(rct.getMaxIdle());
        // 连接池的最大数据库连接数
        jedisPoolConfig.setMaxTotal(rct.getMaxTotal());
        // 最大建立连接等待时间
        jedisPoolConfig.setMaxWaitMillis(rct.getMaxWaitMillis());
        // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
        jedisPoolConfig.setMinEvictableIdleTimeMillis(rct.getMinEvictableIdleTimeMillis());
        // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
        jedisPoolConfig.setNumTestsPerEvictionRun(rct.getNumTestsPerEvictionRun());
        // 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(rct.getTimeBetweenEvictionRunsMillis());
        // 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
        jedisPoolConfig.setTestOnBorrow(rct.getTestOnBorrow());
        // 在空闲时检查有效性, 默认false
        jedisPoolConfig.setTestWhileIdle(rct.getTestOnBorrow());
        return jedisPoolConfig;
    }

    /**
     * Redis集群的配置
     *
     * @return RedisClusterConfiguration
     * @throws
     */
    public RedisClusterConfiguration redisClusterConfiguration() {
        RedisConfigDto rct = getRedisConfigDto();
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        //Set<RedisNode> clusterNodes
        String[] serverArray = rct.getClusterNodes().split(",");
        Set<RedisNode> nodes = new HashSet<RedisNode>();
        for (String ipPort : serverArray) {
            String[] ipAndPort = ipPort.split(":");
            nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1])));
        }
        redisClusterConfiguration.setClusterNodes(nodes);
        redisClusterConfiguration.setMaxRedirects(rct.getMaxRedirects());
        redisClusterConfiguration.setPassword(RedisPassword.of(rct.getPassword()));
        return redisClusterConfiguration;
    }

    /**
     * Redis集群的配置
     *
     * @throws
     */
    public RedisConnectionFactory redisConnectionFactory() {
        RedisConfigDto rcd = getRedisConfigDto();
        RedisStandaloneConfiguration rsc = new RedisStandaloneConfiguration();
        //设置redis服务器的host或者ip地址
        rsc.setHostName(rcd.getHost());
        rsc.setPort(rcd.getPort());
        rsc.setDatabase(rcd.getDatabase());
        rsc.setPassword(rcd.getPassword());
        //获得默认的连接池构造
        //这里需要注意的是，redisConnectionFactoryJ对于Standalone模式的没有（RedisStandaloneConfiguration，JedisPoolConfig）的构造函数，对此
        //我们用JedisClientConfiguration接口的builder方法实例化一个构造器，还得类型转换
        JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcf = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
        //连接池配置
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(rcd.getMaxActive());
        jedisPoolConfig.setMinIdle(rcd.getMinIdle());
        jedisPoolConfig.setMaxIdle(rcd.getMaxIdle());
        jedisPoolConfig.setMaxWaitMillis(rcd.getMaxWait());
        jedisPoolConfig.setTestOnBorrow(rcd.getTestOnBorrow());
        jedisPoolConfig.setEvictorShutdownTimeoutMillis(rcd.getTimeout());
        jpcf.poolConfig(jedisPoolConfig);
        //通过构造器来构造jedis客户端配置
        JedisClientConfiguration jedisClientConfiguration = jpcf.build();
        JedisConnectionFactory factory = new JedisConnectionFactory(rsc, jedisClientConfiguration);
        return factory;
    }

    /**
     * @param
     * @return
     * @Description:redis连接工厂类
     * @date 2018/10/25 19:45
     */
    @Bean
    public RedisConnectionFactory jedisConnectionFactory() {

        RedisConfigDto rcd = getRedisConfigDto();
        Boolean cluster = rcd.getRedisCluster();

        RedisConnectionFactory factory = null;
        if (true == cluster) {
            //集群模式
            factory = new JedisConnectionFactory(redisClusterConfiguration(), getJedisPoolConfig());
        } else {
            factory = redisConnectionFactory();
        }
        return factory;
    }

    /**
     * 实例化 RedisTemplate 对象
     *
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        initDomainRedisTemplate(redisTemplate);
        return redisTemplate;
    }
//
//    /**
//     * 设置数据存入 redis 的序列化方式,并开启事务
//     * 使用默认的序列化会导致key乱码
//     */
//    private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
//        //如果不配置Serializer，那么存储的时候缺省使用String，如果用User类型存储，那么会提示错误User can't cast to String！
//        redisTemplate.setKeySerializer(new StringRedisSerializer());
//        //这个地方有一个问题，这种序列化器会将value序列化成对象存储进redis中，如果
//        //你想取出value，然后进行自增的话，这种序列化器是不可以的，因为对象不能自增；
//        //需要改成StringRedisSerializer序列化器。
//        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
//        redisTemplate.setEnableTransactionSupport(false);
//        redisTemplate.setConnectionFactory(jedisConnectionFactory());
//    }
//

    public RedisTemplate<String, Object> initDomainRedisTemplate(RedisTemplate<String, Object> template) {

        template.setConnectionFactory(jedisConnectionFactory());

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }


    @Bean(name = "redisPubTemplate")
    public StringRedisTemplate redisPubTemplate() {
        // 获取配置
        return new StringRedisTemplate(jedisConnectionFactory());
    }
}
